/* 
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Project: MyMANET - A Platform to Build Customized MANET's
Developers: Ashwin Paranjpe, Santosh Vempala
Georgia Institute of Technology, Atlanta, USA
*/

#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/sched.h>
#include<linux/module.h>
#include<linux/moduleparam.h>
#include<linux/netdevice.h>
#include<linux/etherdevice.h>

#include<linux/string.h>
#include<linux/proc_fs.h>

#include "stat_list_functions.h"
#include "string_functions.h"

int g_stat_list_count;
struct stat_list *stat_head = NULL;

/* 
 * Adds a new mac entry in the stat list or updates the RX/TX packet count depending on the tx_rx_flag
 * tx_rx_flag = 0 for RX
 * tx_rx_flag = 1 for TX
 * */
int add_or_update_stat_entry(uint8_t mac[6], uint8_t tx_rx_flag, uint32_t session_id)
{
	struct stat_list *new = NULL;
	struct stat_list *tmp = NULL;

	if(stat_head == NULL){

		stat_head = (struct stat_list *) kmalloc(sizeof(struct stat_list), GFP_KERNEL);

		memset(stat_head, 0, sizeof(struct stat_list));

                if(stat_head == NULL){
                        printk(KERN_ALERT"\nCould not allocate memory to stat_head\n");
                        return -1;
                }

		stat_head->id = g_stat_list_count;
		memcpy(stat_head->mac, mac, 6);
		if(tx_rx_flag == 0){
			stat_head->session_id = session_id;
			stat_head->num_rx = 1;
			stat_head->last_num_rx = 0;
			stat_head->last_session_id = 0;
		}
		else{
			stat_head->num_tx = 1;	
		}

		stat_head->next = NULL;
		stat_head->prev = NULL;

		g_stat_list_count++;

	}
	else{
		if(search_and_update_stat(mac, tx_rx_flag, session_id) == 1)
			return 0;
		
		/*Entry does not exist. Add it and increment Stat list count.*/
                new = (struct stat_list *) kmalloc(sizeof(struct stat_list), GFP_KERNEL);

		memset(new, 0, sizeof(struct stat_list));

                if(new == NULL){
                        printk(KERN_ALERT"\nCould not allocate memory to a new stat entry\n");
                        return -1;
                }
		
		new->id = g_stat_list_count;
		memcpy(new->mac, mac, 6);
		if(tx_rx_flag == 0){
			new->session_id = session_id;
			new->num_rx = 1;
			new->last_num_rx = 0;
			new->last_session_id = 0;
		}
		else{
			new->num_tx = 1;
		}
	
		tmp = (struct stat_list *)stat_head;
                while(tmp != NULL && tmp->next != NULL){
                        tmp = tmp->next;
                }
                tmp->next = new;
                new->prev = tmp;
                new->next = NULL;

		g_stat_list_count++;

	}
	return 0;
}

int free_entire_stat_list(void){

        struct stat_list *tmp=NULL, *tmp2=NULL;

        if(stat_head == NULL){
                printk(KERN_ALERT"\nStat list is already empty\n");
                return 0;
        }
        tmp = stat_head;
        while(tmp != NULL){
                tmp2 = tmp->next;
                kfree(tmp);
                g_stat_list_count--;
                tmp = tmp2;
        }

        printk(KERN_ALERT"\nFree : Stat list is NOW empty\n");
        return 0;
}

/*
 * If entry exists, entry is updated based on value of tx_rx_flag and return 1
 * tx_rx_flag = 0 for RX
 * tx_rx_flag = 1 for TX
 * else
 * return 0
 * */
uint8_t search_and_update_stat(uint8_t mac[6], uint8_t tx_rx_flag, uint32_t session_id)
{

        struct stat_list *tmp=NULL;

        if(stat_head == NULL){
                return 0;
        }

        tmp = stat_head;

        while(tmp != NULL){

                if((compare_mac_address(tmp->mac, mac)) == 1){

			if(tx_rx_flag == 0){

				/*Check whether session has changed. If yes; reset rx counters*/
				if(session_id != tmp->session_id){
					/*Update the details of the previous session statistics. 
 					*This will be shown in proc entries.*/
					tmp->last_session_id = tmp->session_id;
					tmp->last_num_rx = tmp->num_rx;
					/*Update the new sessions' statistics.*/
					tmp->session_id = session_id;
					tmp->num_rx = 1;
				}
				else{
					tmp->num_rx++;
				}
			}
			else
				tmp->num_tx++;
                        return 1;
                }
                tmp = tmp->next;
        }
        return 0;
}

